---
title: Refresh Token Rotation
excerpt: Obtain new access token from refresh token.
---

NextAuth.js does not handle refresh token rotation automatically. But the functionality can be implemented using callbacks.

1. Create a `refreshAccessToken` helper.

```ts title=pages/api/auth/[...nextauth].js
// Helper to obtain a new access_token from a refresh token.
async function refreshAccessToken(token) {
  try {
    const formData = new URLSearchParams()

    formData.append("grant_type", "refresh_token")
    formData.append("client_id", process.env.OAUTH_CLIENT_ID)
    formData.append("client_secret", process.env.OAUTH_CLIENT_SECRET)
    formData.append("refresh_token", token.refreshToken)

    const response = await fetch(
      `${process.env.NEXT_PUBLIC_DRUPAL_BASE_URL}/oauth/token`,
      {
        method: "POST",
        body: formData,
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        },
      }
    )

    const data = await response.json()

    if (!response.ok) {
      throw new Error()
    }

    return {
      ...token,
      accessToken: data.access_token,
      accessTokenExpires: Date.now() + data.expires_in * 1000,
      refreshToken: data.refresh_token ?? token.refreshToken,
    }
  } catch (error) {
    return {
      ...token,
      error: "RefreshAccessTokenError",
    }
  }
}
```

2. Update our `jwt` and `session` callbacks to handle refresh tokens.

```ts title=pages/api/auth/[...nextauth].js
export default NextAuth({
  callbacks: {
    async jwt({ token, user }) {
      // Forward the access token, refresh token and expiration date from user.
      // We'll use these to handle token refresh.
      if (user) {
        token.accessToken = user.access_token
        token.accessTokenExpires = Date.now() + user.expires_in * 1000
        token.refreshToken = user.refresh_token
      }

      // If token has not expired, return it,
      if (Date.now() < token.accessTokenExpires) {
        return token
      }

      // Otherwise, refresh the token.
      return refreshAccessToken(token)
    },
    async session({ session, token }) {
      if (token?.accessToken) {
        session.accessToken = token.accessToken

        // Decode token and pass info to session.
        const decoded = jwt_decode(token.accessToken)
        session.user.id = decoded.id
        session.user.email = decoded.email
        session.user.username = decoded.username
        session.error = token.error
      }
      return session
    },
  },
})
```
